/*
    jQuery for dynamic grid layout
    Copyright (c) 2010 Keiji Hokamura (Johnnybroccolii, famteam)
    This plugin available for use in all personal or commercial projects under both MIT and GPL licenses.
 */

(function($) {
	var methods = {
		calculatePosition: function(marginRight, marginBottom, offsetTop, adjustHeight, elementClass, hiddenClass, self) {
			var top = 0;
			var left = 0;
			var maxHeight = 0;
			var totalLineWidth = 0;
			var lineElements = [];
			var containerHeight = 0;
//			alert($.gridder.elementsSelector(elementClass, hiddenClass));
			self.children($.gridder.elementsSelector(elementClass, hiddenClass)).each(function(index) {
				var element = $(this);
				
				if (left > 0) {
					left += marginRight;
				}
				
//				alert('Push: ' + index + ', ' + (left + element.width()) + ', ' + self.width());
				if (left + element.width() <= self.width()) {
					left += element.width();
					totalLineWidth += element.width();
					lineElements.push(element);
					if (maxHeight < element.height()) {
						maxHeight = element.height();
					}
				}
				else {
					left = 0;
					var optimizedMarginRight = (self.width() - totalLineWidth) / (lineElements.length - 1);
					for (var i = 0; i < lineElements.length; i++) {
						var lineElement = lineElements[i];
						var lineElementData = {
							toTop: top,
							toLeft: left
						};
						lineElement.data($.gridder.DATA, lineElementData);
						
						left += (lineElement.width() + optimizedMarginRight);
					}
					
					top += (maxHeight + marginBottom);
					left = element.width();
					lineElements = [element];
					totalLineWidth = element.width();
					maxHeight = element.height();
				}
				if (containerHeight < top + element.height()) {
					containerHeight = top + element.height();
				}
			});
			left = 0;
			for (var i = 0; i < lineElements.length; i++) {
				var lineElement = lineElements[i];
				var lineElementData = {
					toTop: top,
					toLeft: left
				};
				lineElement.data($.gridder.DATA, lineElementData);
				
				left += (lineElement.width() + marginRight);
			}
			if (adjustHeight) {
				self.height(containerHeight);
			}
		},
		verticalPosition: function(marginBottom, offsetTop, adjustHeight, elementClass, hiddenClass, self) {
			var top = offsetTop;
			self.children($.gridder.elementsSelector(elementClass, hiddenClass)).each(function(index) {
				var element = $(this);
				element.data($.gridder.DATA, {
					toTop: top,
					toLeft: parseInt(element.css('left'))
				});
				top += (element.height() + marginBottom);
			});
			if (adjustHeight) {
				self.height(top - marginBottom);				
			}
		},
		updatePosition: function(updateHandler, elementClass, hiddenClass, self) {
			if (updateHandler != null) {
				updateHandler(self.children($.gridder.elementsSelector(elementClass, hiddenClass)).css('position', 'absolute').css('margin', 0).css('float', 'none'), self.children('div.' + hiddenClass).filter(function() {
					return $(this).css('display') == 'block';
				}));
			}
		},
		filterByClass: function(classes, elementClass, hiddenClass, newClass, self) {
//			alert(self.attr('id') + ' > ' + 'div.' + elementClass + ': ' + self.children('div.' + elementClass).size());
			self.children('div.' + elementClass).each(function() {
				var element = $(this);
				element.removeClass(newClass);
				if (!element.hasClass(hiddenClass)) {
					if (classes != null && !hasAllClasses(classes, element)) {
						element.addClass(hiddenClass);
					}
				}
				else {
					if (classes == null || hasAllClasses(classes, element)) {
						element.removeClass(hiddenClass).addClass(newClass);
					}
				}
			});
		}
	};
	
	function hasAllClasses(classes, self) {
		if (classes == null) {
			return false;
		}
		var classesArray = classes.split(' ');
		for (var i = 0; i < classesArray.length; i++) {
			if (!self.hasClass(classesArray[i])) {
				return false;
			}
		}
		return true;
	}
	
	$.fn.gridder = function(settings) {
		var defaultSettings = this.data($.gridder.DATA);
		defaultSettings = defaultSettings != null ? defaultSettings : {};
		defaultSettings = $.extend({
			action: $.gridder.ACTION_LAYOUT,
			offsetTop: 0,
			marginRight: 0,
			marginBottom: 0,
			adjustHeight: true,
			updateHandler: function(appearElements, hiddenElement) {
				appearElements.show().each(function() {
					var element = $(this);
					var data = element.data($.gridder.DATA);
					element.css('top', data.toTop).css('left', data.toLeft);
				});
				hiddenElement.hide();
			},
			type: $.gridder.TYPE_BOTH,
			elementClass: 'gridder-element',
			hiddenClass: 'gridder-hidden',
			newClass: 'gridder-new',
			filterClasses: null
		}, defaultSettings);
		settings = $.extend(defaultSettings, settings);
		
		switch (settings.action) {
			case $.gridder.ACTION_LAYOUT:
				switch (settings.type) {
					case $.gridder.TYPE_BOTH:
						methods.calculatePosition(settings.marginRight, settings.marginBottom, settings.offsetTop, settings.adjustHeight, settings.elementClass, settings.hiddenClass, this);
						break;
					case $.gridder.TYPE_VERTICAL:
						methods.verticalPosition(settings.marginBottom, settings.offsetTop, settings.adjustHeight, settings.elementClass, settings.hiddenClass, this);
						break;
				}
				methods.updatePosition(settings.updateHandler, settings.elementClass, settings.hiddenClass, this);
				break;
			case $.gridder.ACTION_CLASS_FILTER:
				methods.filterByClass(settings.filterClasses, settings.elementClass, settings.hiddenClass, settings.newClass, this);
				switch (settings.type) {
					case $.gridder.TYPE_BOTH:
						methods.calculatePosition(settings.marginRight, settings.marginBottom, settings.offsetTop, settings.adjustHeight, settings.elementClass, settings.hiddenClass, this);
						break;
					case $.gridder.TYPE_VERTICAL:
						methods.verticalPosition(settings.marginBottom, settings.offsetTop, settings.adjustHeight, settings.elementClass, settings.hiddenClass, this);
						break;
				}
				methods.updatePosition(settings.updateHandler, settings.elementClass, settings.hiddenClass, this);
				break;
		}
		
		if (this.data($.gridder.DATA) == null) {
			this.data($.gridder.DATA, settings);
		}
		
		return this;
	};
	
	$.gridder = {
		DATA: 'DATA-GRIDDER',
		
		TYPE_BOTH: 'type-both',
		TYPE_VERTICAL: 'type-vertical',
		
		CLASS_ELEMENT: 'gridder-element',
		CLASS_HIDDEN: 'gridder-hidden',
		CLASS_NEW: 'gridder-new',
		
		ACTION_LAYOUT: 'layout',
		ACTION_CLASS_FILTER: 'filter-by-class',
		
		elementsSelector: function(elementClass, hiddenClass) {
			return 'div.' + elementClass + ':not(div.' + hiddenClass + ')';
		}
	};
	
})(jQuery);